package com.medusa.aps.business.modules.security.config;

import com.medusa.aps.business.common.encrypt.CryptHelper;
import com.medusa.aps.business.modules.security.model.SecureExtra;
import com.medusa.aps.business.modules.security.mp.entity.User;
import com.medusa.aps.business.modules.security.mp.service.IUserService;
import com.medusa.aps.business.common.security.model.bean.SecureUser;
import com.medusa.aps.business.common.security.model.enums.SecureCodes;
import com.medusa.aps.business.common.security.resource.exception.SecurityException;
import com.medusa.aps.business.common.security.resource.model.SecureUserAuthentication;
import com.medusa.aps.business.common.security.server.annotation.GrantType;
import com.medusa.aps.business.common.security.server.model.AuthenticationRequest;
import com.medusa.aps.business.common.security.server.provider.IAuthenticationProvider;
import com.medusa.aps.business.global.model.constant.RegexPools;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

/**
 * @author 张治保
 * @since 2023/11/2
 */
@Slf4j
@GrantType("username_password")
@Component
@Lazy
@RequiredArgsConstructor
public class UsernamePasswordAuthenticationProvider implements IAuthenticationProvider<UsernamePasswordAuthenticationProvider.UsernamePasswordRequest> {

    private final IUserService userService;
    private final PasswordEncoder passwordEncoder;
    private final ReloadUserProvider reloadUserProvider;


    @Override
    public Authentication authenticate(Authentication request) throws AuthenticationException {
        return IAuthenticationProvider.super.authenticate(request);
    }

    @Override
    public SecureUser<SecureExtra> authenticate(UsernamePasswordRequest authentication) throws AuthenticationException {
        User user = userService.lambdaQuery()
                .eq(User::getPhone, authentication.getUsername())
                .one();
        String decryptPassword = CryptHelper.decrypt(authentication.getPassword());;
        //校验账号密码
        if (user == null || !passwordEncoder.matches(decryptPassword, user.getPassword())) {
            throw SecurityException.of(SecureCodes.USERNAME_PASSWORD_INVALID);
        }
        SecureUser<SecureExtra> secureUser = reloadUserProvider.loadUser(user);
        //临时缓存上下文信息用于后续操作
        tempContext(authentication.getTokenId(), secureUser);
        return secureUser;

    }

    /**
     * 临时设置上下文
     *
     * @param tokenId    token id
     * @param secureUser 用户信息
     */
    private void tempContext(String tokenId, SecureUser<SecureExtra> secureUser) {
        SecurityContext contextTemp = SecurityContextHolder.createEmptyContext();
        contextTemp.setAuthentication(new SecureUserAuthentication(tokenId, secureUser));
        SecurityContextHolder.setContext(contextTemp);
    }

    /**
     * 用户名密码登录请求表单
     */
    @Getter
    @Setter
    public static class UsernamePasswordRequest extends AuthenticationRequest {
        /**
         * 用户名
         */
        @NotBlank(message = "登录账号不能为空")
        @Pattern(regexp = RegexPools.MOBILE_TEL, message = "登录账号格式错误")
        private String username;

        /**
         * 密码
         */
        @NotBlank(message = "密码不能为空")
        private String password;

    }
}
